package cn.iocoder.yudao.module.trade.service.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.currency.api.rate.ExchangeRateApi;
import cn.iocoder.yudao.module.digital.api.cardkeymanager.ICardKeyManagerApi;
import cn.iocoder.yudao.module.digital.api.cardkeymanager.dto.CardKeySecretDTO;
import cn.iocoder.yudao.module.esim.enums.esasset.EsAssetActiveStateEnum;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.product.api.property.ProductPropertyApi;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.DeliveryMethodEnum;
import cn.iocoder.yudao.module.product.enums.spu.RechargeMethodEnum;
import cn.iocoder.yudao.module.trade.config.TradeOrderMailConfig;
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageItemRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.trade.enums.ApplicationNameEnum;
import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.distinct;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_NOT_EXISTS;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;

/**
 * 交易订单【读】 Service 实现类
 *
 * @author 芋道源码
 */
@Service
public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {

    @Resource
    private ExpressClientFactory expressClientFactory;

    @Resource
    private TradeOrderMapper tradeOrderMapper;

    @Resource
    private TradeOrderItemMapper tradeOrderItemMapper;

    @Resource
    private DeliveryExpressService deliveryExpressService;

    @Resource
    private MemberUserApi memberUserApi;

    @Resource
    private PayOrderApi payOrderApi;

    @Resource
    private ProductSkuApi productSkuApi;

    @Resource
    private ProductSpuApi productSpuApi;

    @Resource
    private ProductPropertyApi productPropertyApi;

    @Resource
    private ExchangeRateApi exchangeRateApi;

    @Value("${spring.application.name}")
    private String applicationName;

    @Resource
    private TradeOrderMailConfig tradeOrderMailConfig;

    @Lazy
    @Resource
    private ICardKeyManagerApi cardKeyManagerApi;

    @Resource
    private TradeOrderLogService tradeOrderLogService;

    // =================== Order ===================

    @Override
    public TradeOrderDO getOrder(Long id) {
        return tradeOrderMapper.selectById(id);
    }

    @Override
    public TradeOrderDO getOrderByNo(String no) {
        return tradeOrderMapper.selectOrderByNo(no);
    }

    @Override
    public TradeOrderDO getOrder(Long userId, Long id) {
        TradeOrderDO order = tradeOrderMapper.selectById(id);
        if (order != null
                && ObjectUtil.notEqual(order.getUserId(), userId)) {
            return null;
        }
        return order;
    }

    @Override
    public TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status) {
        return tradeOrderMapper.selectByUserIdAndCombinationActivityIdAndStatus(userId, combinationActivityId, status);
    }

    @Override
    public List<TradeOrderDO> getOrderList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return tradeOrderMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
        // 根据用户查询条件构建用户编号列表
        Set<Long> userIds = buildQueryConditionUserIds(reqVO);
        if (userIds == null) { // 没查询到用户，说明肯定也没他的订单
            return PageResult.empty();
        }
        if (StringUtils.isNotBlank(reqVO.getCdkCode())) {
            List<String> orderNos = cardKeyManagerApi.getOrderNoByCardKeySecret(reqVO.getCdkCode());
            if (CollectionUtil.isNotEmpty(orderNos)) {
                reqVO.setOrderNoList(orderNos);
            } else {
                return PageResult.empty();
            }
        }
        if (StringUtils.isNotBlank(reqVO.getChannelOrderNo())) {
            PayOrderRespDTO payOrder = payOrderApi.getOrderByChannelOrderNo(reqVO.getChannelOrderNo());
            if (Objects.nonNull(payOrder)) {
                reqVO.setPayOrderId(payOrder.getId());
            } else {
                return PageResult.empty();
            }
        }
        if (Objects.nonNull(reqVO.getStatus())) {
            TradeOrderStatusEnum orderStatusEnum = TradeOrderStatusEnum.fromValue(reqVO.getStatus());// 校验状态
            switch (orderStatusEnum) {
                case UNDELIVERED:
                    reqVO.setShareStatusList(Arrays.asList(TradeOrderShareStatusEnum.NONE.getStatus(), TradeOrderShareStatusEnum.SHARED.getStatus()));
                    break;
                case UN_AUDITED:
                    reqVO.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
                    reqVO.setAuditStatus(TradeOrderAuditStatusEnum.UN_AUDITED.getStatus());
                    reqVO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UN_DELIVERED.getStatus());
                    break;
                case DELIVER_FAILED:
                    reqVO.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
                    reqVO.setAuditStatus(TradeOrderAuditStatusEnum.AUDIT_PASSED.getStatus());
                    reqVO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVER_FAILED.getStatus());
                    break;
                case UN_SHARED:
                    reqVO.setType(TradeOrderTypeEnum.COMBINATION.getType());
                    reqVO.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
                    reqVO.setShareStatus(TradeOrderShareStatusEnum.UN_SHARED.getStatus());
                    reqVO.setCombinationHeadId(0L);
                    break;
                default:
                    break;
            }
        }
        if (ObjectUtil.equals(reqVO.getManual(), TradeOrderManualEnum.MANUAL_HANDLE.getStatus())) {
            reqVO.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
            reqVO.setAuditStatus(null);
            reqVO.setDeliveryStatus(null);
        } else {
            reqVO.setManual(null);
        }
        // 分页查询
        return tradeOrderMapper.selectPage(reqVO, userIds);
    }

    private Set<Long> buildQueryConditionUserIds(TradeOrderPageReqVO reqVO) {
        // 获得 userId 相关的查询
        Set<Long> userIds = new HashSet<>();
        if (StrUtil.isNotEmpty(reqVO.getUserMobile())) {
            MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile());
            if (user == null) { // 没查询到用户，说明肯定也没他的订单
                return null;
            }
            userIds.add(user.getId());
        }
        if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
            List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(reqVO.getUserNickname());
            if (CollUtil.isEmpty(users)) { // 没查询到用户，说明肯定也没他的订单
                return null;
            }
            userIds.addAll(convertSet(users, MemberUserRespDTO::getId));
        }
        return userIds;
    }

    @Override
    public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) {
        // 根据用户查询条件构建用户编号列表
        Set<Long> userIds = buildQueryConditionUserIds(reqVO);
        if (userIds == null) { // 没查询到用户，说明肯定也没他的订单
            return new TradeOrderSummaryRespVO();
        }
        // 查询每个售后状态对应的数量、金额
        List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, null);

        TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L);
        for (Map<String, Object> map : list) {
            Long count = MapUtil.getLong(map, "count", 0L);
            Long price = MapUtil.getLong(map, "price", 0L);
            // 未退款的计入订单，部分退款、全部退款计入售后
            if (TradeOrderRefundStatusEnum.NONE.getStatus().equals(MapUtil.getInt(map, "refundStatus"))) {
                vo.setOrderCount(count).setOrderPayPrice(price);
            } else {
                vo.setAfterSaleCount(vo.getAfterSaleCount() + count).setAfterSalePrice(vo.getAfterSalePrice() + price);
            }
        }
        return vo;
    }

    @Override
    public PageResult<TradeOrderDO> getOrderPage(Long userId, AppTradeOrderPageReqVO reqVO) {
        if (Objects.nonNull(reqVO.getStatus())) {
            TradeOrderStatusEnum orderStatusEnum = TradeOrderStatusEnum.fromValue(reqVO.getStatus());// 校验状态
            switch (orderStatusEnum) {
                case UNDELIVERED:
                    reqVO.setNeShareStatus(TradeOrderShareStatusEnum.UN_SHARED.getStatus());
                    break;
                case UN_SHARED:
                    reqVO.setType(TradeOrderTypeEnum.COMBINATION.getType());
                    reqVO.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
                    reqVO.setShareStatus(TradeOrderShareStatusEnum.UN_SHARED.getStatus());
                    reqVO.setCombinationHeadId(0L);
                    break;
                default:
                    break;
            }
        }
        return tradeOrderMapper.selectPage(reqVO, userId);
    }

    @Override
    public Long getOrderCount(Long userId, Integer status, Boolean commentStatus) {
        return tradeOrderMapper.selectCountByUserIdAndStatus(userId, status, commentStatus);
    }

    @Override
    public List<ExpressTrackRespDTO> getExpressTrackList(Long id, Long userId) {
        // 查询订单
        TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 查询物流
        return getExpressTrackList(order);
    }

    @Override
    public List<ExpressTrackRespDTO> getExpressTrackList(Long id) {
        // 查询订单
        TradeOrderDO order = tradeOrderMapper.selectById(id);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 查询物流
        return getExpressTrackList(order);
    }

    @Override
    public int getSeckillProductCount(Long userId, Long activityId) {
        // 获得订单列表
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId);
        orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }
        // 获得订单项列表
        return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId));
    }

    /**
     * 获得订单的物流轨迹
     *
     * @param order 订单
     * @return 物流轨迹
     */
    private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) {
        if (order.getLogisticsId() == null) {
            return Collections.emptyList();
        }
        // 查询物流公司
        DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId());
        if (express == null) {
            throw exception(EXPRESS_NOT_EXISTS);
        }
        // 查询物流轨迹
        return getSelf().getExpressTrackList(express.getCode(), order.getLogisticsNo(), order.getReceiverMobile());
    }

    /**
     * 查询物流轨迹
     * <p>
     * 缓存的目的：考虑及时性要求不高，但是每次调用需要钱
     *
     * @param code           快递公司编码
     * @param logisticsNo    发货快递单号
     * @param receiverMobile 收、寄件人的电话号码
     * @return 物流轨迹
     */
    @Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile",
            condition = "#result != null")
    public List<ExpressTrackRespDTO> getExpressTrackList(String code, String logisticsNo, String receiverMobile) {
        return expressClientFactory.getDefaultExpressClient().getExpressTrackList(
                new ExpressTrackQueryReqDTO().setExpressCode(code).setLogisticsNo(logisticsNo)
                        .setPhone(receiverMobile));
    }


    // =================== Order Item ===================

    @Override
    public TradeOrderItemDO getOrderItem(Long userId, Long itemId) {
        TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(itemId);
        if (orderItem != null
                && ObjectUtil.notEqual(orderItem.getUserId(), userId)) {
            return null;
        }
        return orderItem;
    }

    @Override
    public TradeOrderItemDO getOrderItem(Long id) {
        return tradeOrderItemMapper.selectById(id);
    }

    @Override
    public List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds) {
        if (CollUtil.isEmpty(orderIds)) {
            return Collections.emptyList();
        }
        return tradeOrderItemMapper.selectListByOrderId(orderIds);
    }

    @Override
    public void getPayOrderExpireTime(List<AppTradeOrderPageItemRespVO> list) {
        list.forEach(order -> {
            PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
            if (payOrder != null) {
                order.setExpireTime(payOrder.getExpireTime());
                if (LocalDateTimeUtils.afterNow(payOrder.getExpireTime())) {
                    long diff = payOrder.getExpireTime().toEpochSecond(ZoneOffset.UTC) - LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
                    order.setExpireTimeCountDown(diff * 1000L);
                } else {
                    order.setExpireTimeCountDown(0L);
                }
            }
            // 计算支付单价
            fillAppItemProps(order.getItems());
            Integer discountPrice = order.getItems().stream().map(AppTradeOrderItemRespVO::getDiscountPrice).reduce(Integer::sum).get();
            order.setDiscountPrice(discountPrice);
        });
    }

    @Override
    public void fillAppItemProps(List<AppTradeOrderItemRespVO> items) {
        Set<Long> spuIds = convertSet(items, AppTradeOrderItemRespVO::getSpuId);
        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);

        Set<Long> skuIds = convertSet(items, AppTradeOrderItemRespVO::getSkuId);
        List<ProductSkuRespDTO> skuList = productSkuApi.getSkuList(skuIds);
        Map<Long, ProductSkuRespDTO> skuMap = convertMap(skuList, ProductSkuRespDTO::getId);
        items.forEach(item -> {
            int price = item.getPrice();
            ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
            if (Objects.nonNull(item.getCustomDenomination()) && item.getCustomDenomination()) {
                if (Objects.nonNull(sku.getDiscountRate()) && sku.getDiscountRate().compareTo(BigDecimal.ZERO) > 0) {
                    int itemNominalValue = BigDecimal.valueOf(price).multiply(new BigDecimal(100)).divide(sku.getDiscountRate(), 0, RoundingMode.HALF_UP).intValue();
                    item.setItemNominalValue(itemNominalValue);
                } else {
                    item.setItemNominalValue(price);
                }
                price = price * item.getNominalValue() / 100;
            } else {
                item.setItemNominalValue(sku.getDenominationFixed());
            }
            int discountPrice = item.getItemNominalValue() * item.getCount() - item.getPayPrice();
            if (discountPrice < 0) {
                discountPrice = 0;
            }
            item.setDiscountPrice(discountPrice);
            Integer value = exchangeRateApi.calculateWithRate(item.getPayPrice(), item.getExchangeRate());
            item.setPaymentPrice(value);
            ProductSpuRespDTO spu = spuMap.get(item.getSpuId());
            item.setCategoryId(spu.getCategoryId());
            List<ProductPropertyValueDetailRespDTO> spuProperty = productPropertyApi.getSpuProperty(item.getSpuId());
            List<AppProductPropertyValueDetailRespVO> properties = TradeOrderConvert.INSTANCE.convertList(spuProperty);
            item.setProperties(properties);
        });
    }

    @Override
    public void fillOrderField(List<TradeOrderPageItemRespVO> list) {
        Map<Long, ProductSkuRespDTO> skuMap = new HashMap<>();
        for (TradeOrderPageItemRespVO vo : list) {
            List<TradeOrderPageItemRespVO.Item> items = vo.getItems();
            for (TradeOrderPageItemRespVO.Item item : items) {
                fillItemProps(skuMap, item);
            }
            fillOrderProps(vo);
        }
    }

    @Override
    public void fillOrderField(TradeOrderDetailRespVO result) {
        List<TradeOrderDetailRespVO.Item> items = result.getItems();
        for (TradeOrderDetailRespVO.Item item : items) {
            fillItemProps(item);
        }
        fillOrderProps(result);
    }

    @Override
    public void fillExpireTime(AppTradeOrderDetailRespVO order) {
        PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
        if (payOrder != null) {
            order.setPayExpireTime(payOrder.getExpireTime());
            if (LocalDateTimeUtils.afterNow(payOrder.getExpireTime())) {
                long diff = payOrder.getExpireTime().toEpochSecond(ZoneOffset.UTC) - LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
                order.setExpireTimeCountDown(diff * 1000L);
            } else {
                order.setExpireTimeCountDown(0L);
            }
        }
    }

    @Override
    public List<TradeOrderDO> getManualOrderList(Duration timeout) {
        LocalDateTime localDateTime = LocalDateTimeUtils.minusTime(timeout);
        return tradeOrderMapper.selectManualOrderList(localDateTime);
    }

    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CDK_VIEW)
    @Override
    public List<CardKeySecretDTO> getCDKByOrderNo(Long userId, String orderNo) {
        TradeOrderDO order = getOrderByNo(orderNo);
        List<CardKeySecretDTO> cdkList = cardKeyManagerApi.getCardkeySecretByOrderNo(userId, orderNo);
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
        return cdkList;
    }

    @Override
    public List<TradeOrderDO> getOrderListByUserId(Long userId, List<Integer> statusList, Integer neStatus, LocalDateTime startTime, LocalDateTime endTime) {
        return tradeOrderMapper.selectList(new LambdaQueryWrapperX<TradeOrderDO>()
                .eq(TradeOrderDO::getUserId, userId)
                .inIfPresent(TradeOrderDO::getStatus, statusList)
                .neIfPresent(TradeOrderDO::getStatus, neStatus)
                .betweenIfPresent(TradeOrderDO::getCreateTime, startTime, endTime)
        );
    }

    @Override
    public List<TradeOrderExportVO> convertExportList(List<TradeOrderPageItemRespVO> resultList) {
        List<TradeOrderExportVO> list = new ArrayList<>(resultList.size());
        for (int i = 0; i < resultList.size(); i++) {
            TradeOrderPageItemRespVO order = resultList.get(i);
            TradeOrderPageItemRespVO.Item item = order.getItems().get(0);
            TradeOrderExportVO vo = new TradeOrderExportVO();
            vo.setId(order.getId());
            vo.setSequenceNumber(i + 1);
            vo.setNo(order.getNo());
            vo.setPayOrderId(order.getPayOrderId());
            //vo.setPayOrderNo(order.getPayOrderNo());
            //vo.setPayChannelOrderNo(order.getPayChannelOrderNo())
            vo.setCreateTime(order.getCreateTime());
            vo.setPayTime(order.getPayTime());
            vo.setDeliveryTime(order.getDeliveryTime());
            //vo.setCdkLookUpTime(order.getCdkLookUpTime());
            vo.setSpuName(item.getSpuName());
            vo.setDenomination(item.getDenomination());
            vo.setPayChannelCode(order.getPayChannelCode());
            vo.setPrice(BigDecimal.valueOf(item.getItemNominalValue()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) + " " + item.getCurrencyCode());
            vo.setProductCount(item.getCount());
            vo.setTotalPrice(BigDecimal.valueOf(item.getItemNominalValue()).multiply(BigDecimal.valueOf(item.getCount())).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) + " " + order.getOrderCurrency());
            vo.setPayPrice(BigDecimal.valueOf(order.getPaymentPrice()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) + " " + order.getPaymentCurrency());
            DeliveryMethodEnum deliveryMethodEnum = DeliveryMethodEnum.fromValue(item.getDeliveryMethod());
            vo.setDeliveryMethod(deliveryMethodEnum.getDesc());
            RechargeMethodEnum rechargeMethodEnum = RechargeMethodEnum.fromValue(item.getRechargeMethod());
            vo.setRechargeMethod(rechargeMethodEnum.getDesc());
            vo.setReceiveEmail(item.getReceiveEmail());
            vo.setTerminal(order.getTerminal());
            vo.setPayChannelCode(order.getPayChannelCode());
            vo.setStatus(order.getStatus());
            list.add(vo);
        }
        return list.parallelStream().map(vo -> {
            PayOrderRespDTO payOrder = payOrderApi.getOrder(vo.getPayOrderId());
            vo.setPayOrderNo(payOrder.getNo());
            vo.setPayChannelOrderNo(payOrder.getChannelOrderNo());
            List<TradeOrderLogDO> orderLogs = tradeOrderLogService.getOrderLogListByOrderId(vo.getId());
            orderLogs.stream()
                    .filter(orderLog -> TradeOrderOperateTypeEnum.MEMBER_CDK_VIEW.getType().equals(orderLog.getOperateType()))
                    .sorted(Comparator.comparing(TradeOrderLogDO::getCreateTime).reversed())
                    .findFirst().ifPresent(orderLog -> vo.setCdkLookUpTime(orderLog.getCreateTime()));
            return vo;
        }).collect(Collectors.toList());
        //return list;
    }

    private void fillOrderProps(TradeOrderBaseVO vo) {
        // 判定需要人工处理
        int manual = vo.checkManual() ? 1 : 0;
        vo.setManual(manual);
        // 计算商品总价, 折算汇率
        Integer totalPaymentPrice = exchangeRateApi.calculateWithRate(vo.getTotalPrice(), vo.getExchangeRate());
        vo.setTotalPaymentPrice(totalPaymentPrice);
        if (Objects.nonNull(vo.getPayOrderId())) {
            PayOrderRespDTO payOrder = payOrderApi.getOrder(vo.getPayOrderId());
            if (Objects.nonNull(payOrder)) {
                vo.setChannelOrderNo(payOrder.getChannelOrderNo());
            }
        }
    }

    private void fillItemProps(TradeOrderItemBaseVO item) {
        Map<Long, ProductSkuRespDTO> skuMap = new HashMap<>();
        fillItemProps(skuMap, item);
    }

    private void fillItemProps(Map<Long, ProductSkuRespDTO> skuMap, TradeOrderItemBaseVO item) {
        if (item.getSkuId() == null) {
            return;
        }
        ProductSkuRespDTO sku;
        if (skuMap.containsKey(item.getSkuId())) {
            sku = skuMap.get(item.getSkuId());
        } else {
            sku = productSkuApi.getSku(item.getSkuId());
            if (sku != null) {
                skuMap.put(item.getSkuId(), sku);
            }
        }
        if (sku != null) {
            item.setDenomination(sku.getDenomination());
            item.setSkuName(sku.getName());
        }
        int price = item.getPrice();
        if (Objects.nonNull(item.getCustomDenomination()) && item.getCustomDenomination()) {
            if (Objects.nonNull(sku.getDiscountRate()) && sku.getDiscountRate().compareTo(BigDecimal.ZERO) > 0) {
                int itemNominalValue = BigDecimal.valueOf(price).multiply(new BigDecimal(100)).divide(sku.getDiscountRate(), 0, RoundingMode.HALF_UP).intValue();
                item.setItemNominalValue(itemNominalValue);
            }
            price = price * item.getNominalValue() / 100;
        } else {
            item.setItemNominalValue(sku.getDenominationFixed());
        }

        Integer value = exchangeRateApi.calculateWithRate(item.getPayPrice(), item.getExchangeRate());
        item.setPaymentPrice(value);
    }

    @Override
    public Integer checkCombinationOrderStatus(Integer type, Integer status, Long combinationHeadId, Integer shareStatus) {
        if (TradeOrderTypeEnum.isCombination(type)
                && TradeOrderStatusEnum.isUndelivered(status)
                && ObjectUtil.equals(0L, combinationHeadId)
                && TradeOrderShareStatusEnum.isUnShared(shareStatus)
        ) {
            status = TradeOrderStatusEnum.UN_SHARED.getStatus();
        }
        return status;
    }

    @Override
    public void translateOrderStatus(TradeOrderDO order) {
        checkCombinationOrderStatus(order);
    }

    private void checkCombinationOrderStatus(TradeOrderDO order) {
        Integer status = checkCombinationOrderStatus(order.getType(), order.getStatus(), order.getCombinationHeadId(), order.getShareStatus());
        order.setStatus(status);
    }

    private void checkActiveOrderStatus(TradeOrderDO order) {
        Integer status = checkActiveOrderStatus(order.getStatus(), order.getActiveState());
        order.setStatus(status);
    }

    private Integer checkActiveOrderStatus(Integer status, Integer activeState) {
        if (TradeOrderStatusEnum.isCompleted(status)) {
            if (EsAssetActiveStateEnum.isActivated(activeState)) {
                status = TradeOrderStatusEnum.PENDING_ACTIVATION.getStatus();
            } else {
                status = TradeOrderStatusEnum.ACTIVATED.getStatus();
            }
        }
        return status;
    }

    @Override
    public void translateOrderStatus(List<TradeOrderDO> orders) {
        for (TradeOrderDO order : orders) {
            translateOrderStatus(order);
        }
    }

    @Override
    public List<TradeOrderDO> getCombinationOrderList(Long headId) {
        return tradeOrderMapper.getCombinationOrderList(headId);
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private TradeOrderQueryServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
